<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="utf-8" />
    <meta name="referrer" content="same-origin" />
    <meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
    <meta name="”applicable-device”" content="”pc,mobile”" />
    <link rel="stylesheet" href="https://fastly.jsdelivr.net/gh/PrismJS/prism-themes/themes/prism-one-light.css" />

    <style type="text/css">
        * {
            margin: 0;
            padding: 0;
        }

        .code-container * {
            font-size: 14px;
        }

        .main {
            margin: 0 auto;
            width: 800px;
            max-width: 90%;
        }

        .input-textarea {
            font: 400 14px Source Code Pro, DejaVu Sans Mono, Ubuntu Mono, Anonymous Pro, Droid Sans Mono, Menlo, Monaco, Consolas, Inconsolata, Courier, monospace, PingFang SC, Microsoft YaHei, sans-serif !important;
            width: 100%;
            height: 300px;
            display: block;
            box-sizing: border-box;
            padding: 6px 8px;
            border-color: #eee;
            border-width: 1px;
            border-style: solid;
        }

        .input-textarea:focus {
            outline: none !important;
            border-color: #d2d2d2 !important;
        }

        .btn-primary {
            font-family: "'微软雅黑','Helvetica Neue',Helvetica,Arial,sans-serif";
            font-size: 13px !important;
            line-height: 18px !important;
            padding: 5px 16px;
            display: inline-block;
            vertical-align: middle;
            font-weight: normal;
            border-radius: 3px;
            margin: 0 8px 0 3px;
            background-color: #00d1b2;
            border-color: transparent;
            color: #ffffff;
            cursor: pointer;
        }

        .btn-primary:hover {
            color: #fff;
            background-color: #00c4a7;
            border-color: transparent;
        }

        .btn-warning {
            font-family: "'微软雅黑','Helvetica Neue',Helvetica,Arial,sans-serif";
            font-size: 13px !important;
            line-height: 18px !important;
            padding: 5px 16px;
            display: inline-block;
            vertical-align: middle;
            font-weight: normal;
            border-radius: 3px;
            margin: 0 8px 0 3px;
            color: #fff;
            background-color: #f0ad4e;
            border-color: #eea236;
            cursor: pointer;
        }

        .btn-warning:hover {
            color: #fff;
            background-color: #ec971f;
            border-color: #d58512;
        }

        .code-container:hover .hljs-button {
            display: block
        }

        .hljs-button {
            font: 400 14px Source Code Pro, DejaVu Sans Mono, Ubuntu Mono, Anonymous Pro, Droid Sans Mono, Menlo, Monaco, Consolas, Inconsolata, Courier, monospace, PingFang SC, Microsoft YaHei, sans-serif !important;
            z-index: 10;
            display: none;
            position: absolute;
            right: 8px;
            top: 5px;
            font-size: 12px;
            color: #ffffff;
            background-color: #9999AA;
            padding: 2px 6px;
            border-radius: 3px;
            cursor: pointer;
            box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05), 0 2px 4px rgba(0, 0, 0, 0.05);
        }

        .hljs-button:after {
            content: attr(data-title)
        }
    </style>
    <title>Markdown更换图床</title>
    <link rel="icon" type="image/png" sizes="64x64" href="https://static.dingtalk.com/media/lALPJwnIv8IEPhpAQA_64_64.png" />

    <!-- <link rel="stylesheet" href="https://prismjs.com/themes/prism-okaidia.css" /> -->
    <script src="https://fastly.jsdelivr.net/gh/Nomeqc/static/js/prism/1.25.0/prism.js"></script>
    <script src="https://cdn.staticfile.org/turndown/7.1.1/turndown.min.js"></script>
    <script src="https://cdn.staticfile.org/showdown/1.9.1/showdown.min.js"></script>
    <script src="https://fastly.jsdelivr.net/gh/Nomeqc/static@latest/js/img-upload/dingtalk.upload.js"></script>

</head>

<body>
    <div style="height: 25px;"></div>
    <div class="main">
        <textarea class="input-textarea" id="in" placeholder="请输入Markdown内容"></textarea>

        <div style="text-align: center; position: relative;">
            <span style="display: inline-block;padding-top: 10px; padding-bottom: 10px; ">
                <div id="transfer" class="btn-primary">转换</div>
                <div id="clear" class="btn-warning">清空</div>
            </span>
            <div style="display: inline-block;position: absolute;font-family: &quot;'微软雅黑','Helvetica Neue',Helvetica,Arial,sans-serif&quot;;font-size: 13px !important;padding-left: 20px;top: 0;bottom: 0;margin: auto;line-height: 30px;height: 30px;"
                id="status"></div>
        </div>

        <div style="position: relative;" id="code-container">
            <div class="hljs-button" data-title="复制" id="copy-btn"></div>
            <pre style="max-height: 450px;"><code class="language-markdown" id="code"></code></pre>
        </div>
    </div>

    <script>
        let scrollBarWidth;
        let markdownResult = '';
        let localServerUrl;

        async function getLocalServerUrl() {
            if (localServerUrl !== undefined) {
                return localServerUrl
            }
            let timeoutPromise = (timeout) => {
                return new Promise((resolve, reject) => {
                    setTimeout(() => {
                        resolve('');
                    }, timeout);
                });
            }
            let requestPromise = (url) => {
                return new Promise((resolve, reject) => {
                    fetch(url).then(resp => {
                        resolve(url)
                    }).catch(err => {
                        resolve('')
                    })
                });
            }

            let url = 'http://127.0.0.1:8080/'
            localServerUrl = await Promise.race([timeoutPromise(5000), requestPromise(url)])
            return localServerUrl
        }

        function copyToClipboard(text) {
            if (window.clipboardData && window.clipboardData.setData) {
                // Internet Explorer-specific code path to prevent textarea being shown while dialog is visible.
                return window.clipboardData.setData("Text", text);
            }
            if (document.queryCommandSupported && document.queryCommandSupported("copy")) {
                var textarea = document.createElement("textarea");
                textarea.textContent = text;
                // Prevent scrolling to bottom of page in Microsoft Edge.
                textarea.style.position = "fixed";
                document.body.appendChild(textarea);
                textarea.select();
                try {
                    // Security exception may be thrown by some browsers.
                    return document.execCommand("copy");
                } catch (ex) {
                    console.warn("Copy to clipboard failed.", ex);
                    return false;
                } finally {
                    document.body.removeChild(textarea);
                }
            }
            return false;
        }


        function getScrollBarWidth() {
            if (scrollBarWidth !== undefined) return scrollBarWidth;

            const outer = document.createElement('div');
            outer.className = 'el-scrollbar__wrap';
            outer.style.visibility = 'hidden';
            outer.style.width = '100px';
            outer.style.position = 'absolute';
            outer.style.top = '-9999px';
            document.body.appendChild(outer);

            const widthNoScroll = outer.offsetWidth;
            outer.style.overflow = 'scroll';

            const inner = document.createElement('div');
            inner.style.width = '100%';
            outer.appendChild(inner);

            const widthWithScroll = inner.offsetWidth;
            outer.parentNode.removeChild(outer);
            scrollBarWidth = widthNoScroll - widthWithScroll;

            return scrollBarWidth;
        };

        function findImg(nodes) {
            let imgNodes = []
            let find = (nodes) => {
                for (let n of nodes) {
                    if (n.tagName == "IMG") {
                        imgNodes.push(n)
                    }
                    if (n.hasChildNodes) {
                        find(n.childNodes)
                    }
                }
            }
            find(nodes)
            return imgNodes
        }

        function parseElements(htmlString) {
            return new DOMParser().parseFromString(htmlString, 'text/html').body.childNodes;
        }

        function image2base64(img, mime_type) {
            // New Canvas
            var canvas = document.createElement('canvas');
            canvas.width = img.width;
            canvas.height = img.height;
            // Draw Image
            var ctx = canvas.getContext('2d');
            ctx.drawImage(img, 0, 0);
            // To Base64
            return canvas.toDataURL(mime_type);
        }

        function detectImageFormat(base64Str) {
            const formatMap = {
                '/9j/': 'jpg',
                'R0lGO': 'gif',
                'iVBORw': 'png',
                'Qk': 'bmp'
            }
            let key = Object.keys(formatMap).find(item => base64Str.startsWith(item))
            return key ? formatMap[key] : ''
        }

        function uploadImages(images, callback) {
            let uploaded = []
            if (images.length == 0) {
                if (typeof callback == "function") {
                    callback(uploaded)
                }
                return
            }
            function download(imgUrl) {
                return new Promise((resolve, reject) => {
                    getLocalServerUrl().then(serverUrl => {
                        let img = new Image();
                        if (serverUrl) {
                            img.src = `${serverUrl}?url=${encodeURIComponent(imgUrl)}`
                        } else {
                            img.src = `https://images.weserv.nl/?url=${encodeURIComponent(imgUrl)}`
                        }

                        img.setAttribute("crossOrigin", 'Anonymous');
                        img.onload = function () {
                            var height = this.height;
                            var width = this.width;
                            if (height > 0 || width > 0) {
                                let base64Str = image2base64(this, '').split(',')[1]
                                let format = detectImageFormat(base64Str)
                                let size = { "height": height, "width": width }
                                if (format) {
                                    let file = { "image": this, "base64": base64Str, "format": format, "size": size }
                                    resolve(file)
                                } else {
                                    console.log("不支持的图片格式")
                                    reject("不支持的图片格式")
                                }
                                return true;
                            }

                            return false;
                        }
                        img.onerror = function () {
                            reject("图片加载出错")
                        }
                    })

                })
            }

            function updateProgressText(progressText) {
                let text = `图片共${images.length}张，正在上传第${uploaded.length + 1}张... ${progressText}`
                document.querySelector("#status").innerText = text
            }

            function upload(file) {
                return new Promise((resolve, reject) => {
                    let imgWidth = file.size.width
                    let imgHeight = file.size.height
                    let format = file.format
                    let image = file.image
                    let uploader = new DingtalkImageUploader(file.base64, mediaId => {
                        let imageUrl = `https://static.dingtalk.com/media/${mediaId}_${imgWidth}_${imgHeight}.${format}`
                        uploaded.push(imageUrl)
                        console.log('图片地址：', imageUrl)
                        resolve(imageUrl)
                    }, progress => {
                        updateProgressText(Math.round(progress * 100) + '%')
                        console.log(`进度：${Math.round(progress * 100)}%`)
                    }, error => {
                        reject(error)
                        updateProgressText(error);
                        console.log('upload error:' + error)
                    });
                    uploader.upload();
                })
            }

            const queue = async (images) => {
                let success = {}
                let number = 0
                for (let image of images) {
                    number++
                    let text = `图片共${images.length}张，正在准备上传第${number}张...`
                    document.querySelector("#status").innerText = text
                    try {
                        if (success[image.src]) {
                            uploaded.push(success[image.src])
                            image.src = success[image.src]
                            continue
                        }
                        let file = await download(image.src)
                        let imageUrl = await upload(file)
                        success[image.src] = imageUrl
                        image.src = imageUrl
                    } catch (e) {
                        let text = `图片共${images.length}张，正在上传第${number}张... 出错了：${e}`
                        document.querySelector("#status").innerText = text
                        console.log(e)
                    }
                }
                return success
            }
            queue(images).then(success => {
                if (typeof callback == "function") {
                    callback(success)
                }
            })
        }

        function highlightCode(str) {
            let code = document.querySelector('code')
            var newCode = document.createElement('code');
            newCode.textContent = str;
            newCode.className = code.className;

            Prism.highlightElement(newCode);

            code.parentElement.replaceChild(newCode, code);
            newCode.setAttribute('id', 'code')
        };

        function adjustCopyButtonPosition() {
            let pre = document.querySelector("#code-container > pre")
            let right = 8 + (pre.scrollHeight > pre.clientHeight ? getScrollBarWidth() : 0)
            document.querySelector("#copy-btn").style.right = `${right}px`
        }

        document.querySelector("#transfer").addEventListener('click', function () {
            let input = document.querySelector("#in").value
            if (input.trim().length == 0) {
                return
            }

            let converter = new showdown.Converter()
            let html = converter.makeHtml(input);
            let nodes = parseElements(html)
            let imgNodes = findImg(nodes)

            let getURLHostName = (s) => {
                try {
                    return (new URL(s)).hostname
                } catch (e) {
                    return ''
                }
            }
            imgNodes = imgNodes.filter(ele => getURLHostName(ele.src) != 'static.dingtalk.com')

            uploadImages(imgNodes, function (success) {
                let container = document.createElement("p")
                for (let node of nodes) {
                    container.appendChild(node)
                }
                //codeBlockStyle: 代码块用```， 大标题用#
                const turndownService = new TurndownService({ codeBlockStyle: 'fenced', headingStyle: 'atx', 'preformattedCode': true })
                let markdown = turndownService.turndown(container.innerHTML)
                markdownResult = markdown
                if (markdown.length > 0) {
                    document.querySelector('#code-container').classList.add('code-container')
                } else {
                    document.querySelector('#code-container').classList.remove('code-container')
                }
                highlightCode(markdown)
                adjustCopyButtonPosition()
                document.querySelector("#status").innerText = ""
            })
        })

        document.querySelector("#clear").addEventListener('click', function () {
            document.querySelector("#in").value = ""
            document.querySelector("#status").innerText = ""
        })

        document.querySelector("#copy-btn").addEventListener('click', function () {
            let btnTitle = copyToClipboard(markdownResult ? markdownResult : '') ? "复制成功" : "复制失败"
            document.querySelector("#copy-btn").setAttribute('data-title', btnTitle)
            setTimeout(function () {
                document.querySelector("#copy-btn").setAttribute('data-title', '复制')
            }, 1000)
        })

    </script>
</body>

</html>